/*
 * Copyright 2009-2010 by The Regents of the University of California
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may obtain a copy of the License from
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.ucr.cs.dblab.acestore.api;

import java.nio.ByteBuffer;
import edu.ucr.cs.dblab.acestore.base.datatypes.IntegerSerDeserializer;

/**
 * @author jarodwen
 * @created May 2, 2011
 */
public class TupleInterpreter {

    private int[] endOffsets;

    private byte[][] fields;

    private int fieldCount;

    private final int INTSIZE = IntegerSerDeserializer.getSingleton()
            .getTypeSize();

    private int workingField;

    public TupleInterpreter(int fieldCount) {
        this.fieldCount = fieldCount;
        this.endOffsets = new int[fieldCount];
        this.fields = new byte[fieldCount][];
        this.workingField = 0;
    }

    public TupleInterpreter(byte[] buf) {
        ByteBuffer bbuf = ByteBuffer.wrap(buf);
        this.fieldCount = bbuf.getInt(0);
        this.endOffsets = new int[fieldCount];
        this.fields = new byte[fieldCount][];
        int fieldStart = 0;
        for (int i = 0; i < fieldCount; i++) {
            endOffsets[i] = bbuf.getInt(INTSIZE + i * INTSIZE);
            int fieldLength = endOffsets[i] - fieldStart + 1;
            byte[] field = new byte[fieldLength];
            bbuf.get(field, INTSIZE + fieldCount * INTSIZE + fieldStart,
                    fieldLength);
            fields[i] = field;
            fieldStart += fieldLength;
        }
        this.workingField = fieldCount;
    }

    public TupleInterpreter(byte[] buf, int offset, int length) {
        ByteBuffer bbuf = ByteBuffer.wrap(buf, offset, length);
        this.fieldCount = bbuf.getInt(0);
        this.endOffsets = new int[fieldCount];
        this.fields = new byte[fieldCount][];
        int fieldStart = 0;
        for (int i = 0; i < fieldCount; i++) {
            endOffsets[i] = bbuf.getInt(INTSIZE + i * INTSIZE);
            int fieldLength = endOffsets[i] - fieldStart + 1;
            byte[] field = new byte[fieldLength];
            bbuf.position(offset + INTSIZE + fieldCount * INTSIZE + fieldStart);
            bbuf.get(field, 0, fieldLength);
            fields[i] = field;
            fieldStart += fieldLength;
        }
        this.workingField = fieldCount;
    }

    public void reset() {
        this.endOffsets = new int[fieldCount];
        this.fields = new byte[fieldCount][];
        this.workingField = 0;
    }
    
    public void parse(byte[] buf, int offset, int length) {
        ByteBuffer bbuf = ByteBuffer.wrap(buf, offset, length);
        this.fieldCount = bbuf.getInt(0);
        this.endOffsets = new int[fieldCount];
        this.fields = new byte[fieldCount][];
        int fieldStart = 0;
        for (int i = 0; i < fieldCount; i++) {
            endOffsets[i] = bbuf.getInt(INTSIZE + i * INTSIZE);
            int fieldLength = endOffsets[i] - fieldStart + 1;
            byte[] field = new byte[fieldLength];
            bbuf.position(offset + INTSIZE + fieldCount * INTSIZE + fieldStart);
            bbuf.get(field, 0, fieldLength);
            fields[i] = field;
            fieldStart += fieldLength;
        }
        this.workingField = fieldCount;
    }

    public void addField(byte[] field) throws Exception {
        if (workingField >= fieldCount) {
            throw new Exception(
                    "Tuple build tried to add a field out of the bound.");
        }
        if (workingField == 0) {
            endOffsets[workingField] = field.length - 1;
        } else {
            endOffsets[workingField] = field.length
                    + endOffsets[workingField - 1];
        }
        fields[workingField] = field;
        workingField++;
    }

    public byte[] buildTuple() {
        int tupleLength = INTSIZE + endOffsets[endOffsets.length - 1] + 1
                + endOffsets.length * INTSIZE;
        byte[] tupleInBinary = new byte[tupleLength];
        ByteBuffer buf = ByteBuffer.wrap(tupleInBinary);
        // 1: field count
        buf.putInt(fieldCount);
        // 2: end offset for each field
        for (int eo : endOffsets) {
            buf.putInt(eo);
        }
        // 3: each field
        for (byte[] b : fields) {
            buf.put(b);
        }
        return tupleInBinary;
    }
    
    public int getFieldCount(){
        return fieldCount;
    }
    
    public byte[] getField(int fidx){
        return fields[fidx];
    }
    
    public String toString(){
        StringBuilder sbder = new StringBuilder();
        for(int i = 0; i < this.fieldCount; i++){
            sbder.append(getByteString(fields[i]));
        }
        return sbder.toString();
    }
    
    private String getByteString(byte[] array){
        StringBuilder sbder = new StringBuilder();
        sbder.append("[");
        for(int i = 0; i < array.length; i++){
            sbder.append(array[i]);
            if(i < array.length - 1)
                sbder.append(",");
        }
        sbder.append("]");
        return sbder.toString();
    }
}
